import pyttsx3
import os
import sys
import comtypes.client
from pptx import Presentation
# from moviepy.editor import ImageClip, AudioFileClip, concatenate_videoclips
from moviepy import ImageClip, AudioFileClip, TextClip, VideoFileClip,concatenate_videoclips, CompositeVideoClip
from datetime import datetime

#文字转语音
def text_to_audio(textfile, output_folder):
    # 初始化语音引擎
    engine = pyttsx3.init()
    # 设置语速
    engine.setProperty('rate', 140)  # 调整语速
    # 设置音量
    engine.setProperty('volume', 0.9 )
    engine.setProperty('voice', 'zh-CN')  # 设置语音为中文

    audio_files = []

    # 打开文件
    with open(textfile, 'r', encoding='utf-8') as file:
        # 读取文件内容到列表
        # text_list = file.readlines()
        text_list = [line.strip() for line in file.readlines() if line.strip()]
        # 遍历文本列表
    for i, text in enumerate(text_list):
        # 生成音频文件路径
        audio_path = os.path.join(output_folder, f"slide_{i}.mp3")
        # 如果文本为空，朗读一个空格，生成一个极短音频作为占位
        if not text.strip():
            # 如果为空页，朗读一个空格，生成一个极短音频作为占位
            engine.save_to_file(" ", audio_path)
        else:
            engine.save_to_file(text, audio_path)
        audio_files.append(audio_path)
    engine.runAndWait()
    return audio_files

#获取图片名称存为列表
# def get_images(image_folder):
#     # 获取保存的图片文件列表
#     #如果保存图片时是slide_0.jpg这种格式用这个
#     # image_files = sorted(
#     #     [os.path.join(image_folder, f) for f in os.listdir(image_folder) if f.endswith((".jpg",".JPG"))],
#     #     key=lambda x: int(os.path.splitext(os.path.basename(x))[0].split("_")[-1])
#     # )
#     #如果保存图片时是slide1.jpg这种格式用这个
#     image_files = sorted(
#         [os.path.join(image_folder, f) for f in os.listdir(image_folder) if f.endswith((".jpg", ".JPG", ".png", ".PNG", ".jpeg", ".JPEG", ".gif", ".GIF", ".bmp", ".BMP"))],
#         key=lambda x: int(''.join(filter(str.isdigit, os.path.splitext(os.path.basename(x))[0])))
#     )
#
#     # 返回图片文件列表
#     return image_files

def get_media_files(media_folder):
    """
    获取媒体文件列表（支持图片和视频）
    按文件名中的数字排序（如 slide1.jpg, video1.mp4）
    """
    media_files = []
    for f in os.listdir(media_folder):
        # 判断文件是否为图片或视频格式
        if f.lower().endswith((".jpg", ".jpeg", ".png", ".mp4", ".mov", ".avi")):
            media_files.append(os.path.join(media_folder, f))

    # 按文件名开头的数字排序，文件名要求为1.xxx.jpg或2.xxx.mp4格式
    def extract_number(path):
        filenumber = os.path.basename(path).split('.')[0]
        return int(''.join(filter(str.isdigit, filenumber)) or 0)

    return sorted(media_files, key=extract_number)

def create_typing_effect(
    text,
    start_time,
    duration,
    fontsize=24,
    font="C:/Windows/Fonts/simhei.ttf",
    max_width=800,
    left_margin=50,
    bottom_offset=50,  # 字幕距离底部的距离（单位：像素
):
    """
      创建逐字显示的字幕片段，支持：
    - 从左到右打字效果（所有行都从左边开始）
    - 自动换行
    - 已出现文字自动上移（逐行显示）
    - 每段结束后停顿一段时间
    """
    clips = []
    current_line = ""
    lines = []  # 存储已完成的整行文本
    word_duration = duration / len(text) if len(text) > 0 else 0.1  # 每个字的间隔时间

    for i, char in enumerate(text):
        current_line += char
        # 测试当前行是否超出最大宽度
        test_clip = TextClip(
            text=current_line + "X",  # 加个 X 避免截断判断不准
            font_size=fontsize,
            font=font,
            size=(max_width, None),
            method='caption'   # 使用 caption 来实现自动换行 + 固定布局
        )

        # 如果当前行超出宽度，则换行
        if test_clip.w > max_width:
            current_line = current_line[:-1]  # 去掉最后一个字符
            lines.append(current_line)
            new_line = char
            current_line = new_line

        # 构建当前完整显示的文本（包含历史行）
        full_text = "\n".join(lines + [current_line])

        # 创建 TextClip
        clip = TextClip(
            text=full_text,
            font_size=fontsize,
            color='yellow',
            bg_color=(0, 0, 0, 0),
            font=font,
            size=(max_width, None),  # 设置最大宽度，自动换行
            method='caption'  # 自动换行 + 固定布局
        ).with_position("center", lambda t: 720 - bottom_offset) \
            .with_start(start_time + i * word_duration) \
            .with_duration(word_duration)  #  从左边开始显示

        clips.append(clip)


    return clips

#将图片和音频合成视频。每一页图片和音频对应,最后吧字幕也添加进来
def create_video_from_images_and_audios(textfile, media_files, audio_files, output_video, pause_duration=2.0):
      # 创建一个空列表，用于存储视频片段
    video_clips = []
    # 创建一个空列表，用于存储字幕片段
    subtitles = []
    # 读取文本内容，用于生成字幕
    with open(textfile, 'r', encoding='utf-8') as f:
        # 将文本内容按行读取，并去除空行
        text_lines = [line.strip() for line in f.readlines() if line.strip()]
    # 初始化当前时间为0.0
    current_time = 0.000

    # 遍历图片文件和音频文件
    for i, (media_file, audio_file) in enumerate(zip(media_files, audio_files)):
        # 判断是图片还是视频
        ext = os.path.splitext(media_file)[1].lower()
        is_image = ext in [".jpg", ".jpeg", ".png"]
        is_video = ext in [".mp4", ".mov", ".avi"]

        # 读取已有的音频文件片段并获取时长
        audio_clip = AudioFileClip(audio_file)
        audio_duration = audio_clip.duration
        # 设置每页总时长 = 音频时长 + 停顿时长
        # pause_duration = 2.0  #  可调整
        total_duration = audio_duration + pause_duration

        # 构建媒体片段
        if is_image:
            media_clip = ImageClip(media_file).resized((1280, 720))
        elif is_video:
            media_clip = VideoFileClip(media_file).without_audio()
        else:
            continue

        # 如果是视频，使用原始时长，否则使用音频时长+停顿时长
        clip_duration = media_clip.duration if is_video and (media_clip.duration > total_duration) else total_duration
        media_clip = media_clip.with_duration(clip_duration).with_audio(audio_clip).with_fps(24)

            # 将视频片段添加到列表中
        video_clips.append(media_clip)

        # 创建逐字显示的字幕片段
        if i < len(text_lines):
            text = text_lines[i]
            # 调用 create_typing_effect 函数，生成逐字显示的字幕片段
            typing_clips = create_typing_effect(
                text=text,
                start_time=current_time,
                fontsize=30,
                font="C:/Windows/Fonts/simhei.ttf",
                max_width = 600,
                duration=audio_duration,
                left_margin = 30,  # 可选：文字离左边距离
                bottom_offset=50,  # 设置字幕离底部的距离（单位：像素）

            )
            # 将字幕片段添加到列表中
            subtitles.extend(typing_clips)

        # 更新当前时间
        current_time += clip_duration


    # 合成最终视频（按顺序拼接）
    final_video_clip = concatenate_videoclips(video_clips, method="chain")
    #将字幕叠加到视频上
    final_video_with_subtitles = CompositeVideoClip([final_video_clip] + subtitles)
    # 导出视频
    final_video_with_subtitles.write_videofile(output_video, codec="libx264", audio_codec="libmp3lame", fps=24)

#主函数
def main(speed_text, output_folder, output_video,pause_duration):
    # 创建输出文件夹
    os.makedirs(output_folder, exist_ok=True)
    # 创建输出图片文件夹
    os.makedirs(os.path.join(output_folder, "images"), exist_ok=True)

    # 文字转语音
    # 检查 speed_text 文件是否存在
    if not os.path.isfile(speed_text):
        print("错误：旁白文件不存在！")
        sys.exit()
    print("1. 文字转语音...")
    audio_files = text_to_audio(speed_text, output_folder)
    print("文字转语音--OK")
    # 图片转视频
    print("2. 图片转视频...")
    image_folder = os.path.join(output_folder, "images")
    image_files = get_media_files(image_folder)
    # 检查 image_files 是否非空
    if not image_files:
        print("错误：未找到任何媒体文件！请检查 images 文件夹内容")
        sys.exit()
    print("图片转视频--OK")
    # 合成视频
    print("3. 合成视频...")
    create_video_from_images_and_audios(speed_text, image_files, audio_files, output_video, pause_duration)
    print("合成视频--OK")

if __name__ == "__main__":
    output_dir = r"C:\Users\hexq\PycharmProjects\mypy\video\qiheqixiangjv"
    output_video = os.path.join(output_dir, f"video_{datetime.now().strftime('%Y%m%d%H%M%S')}.mp4")
    speed_text = r"C:\Users\hexq\PycharmProjects\mypy\video\qiheqixiangjv\speed_text.txt"
    pause = 2.0  # 每页结束后暂停时间，可调整

    main(speed_text, output_dir, output_video, pause)
